/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2012-2020. All rights reserved.
 * Description:
 * Author: huawei
 * Create: 2018-05-14
 */
#include <linux/init.h>
#include <linux/module.h>
#include <asm/setup.h>
#include <linux/securec.h>

#include "drv_log.h"
#include "spi_ids.h"

#define SPI_DRV_VERSION "1.22"
/* *************************************************************************** */
#define SPIIDS_LOG_PREFIX "spi_ids"
#define DBG_MSG(fmt...) do {                                  \
        drv_info(SPIIDS_LOG_PREFIX, fmt); \
    } while (0)
#define DBG_BUG(fmt...) do {                                 \
        drv_err(SPIIDS_LOG_PREFIX, fmt); \
    } while (0)

#ifndef ASSERT_RET
#define ASSERT_RET(a, ret) {                                  \
        if (!(a)) {                    \
            DBG_BUG("return error\n"); \
            return (ret);              \
        }                              \
    }
#endif


#ifndef ASSERT_NOT_RET
#define ASSERT_NOT_RET(a) {                                  \
        if (!(a)) {                    \
            DBG_BUG("return error\n"); \
            return;                    \
        }                              \
    }
#endif

/* *************************************************************************** */
#define SET_READ_STD(_dummy_, _size_, _clk_)                                                                          \
    static struct spi_operation read_std_##_dummy_##_size_##_clk_ = { SPI_IF_READ_STD, SPI_CMD_READ, _dummy_, _size_, \
        _clk_ }

#define SET_READ_STD_4B(_dummy_, _size_, _clk_)                                                                        \
    static struct spi_operation read_std_##_dummy_##_size_##_clk_##_4b_ = { SPI_IF_READ_STD, SPI_CMD_READ_4B, _dummy_, \
        _size_, _clk_ }


#define READ_STD(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_
#define READ_STD_4B(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_##_4b_

// -----------------------------------------------------------------------------
#define SET_READ_FAST(_dummy_, _size_, _clk_)                                                                        \
    static struct spi_operation read_fast_##_dummy_##_size_##_clk_ = { SPI_IF_READ_FAST, SPI_CMD_FAST_READ, _dummy_, \
        _size_, _clk_ }

#define SET_READ_FAST_4B(_dummy_, _size_, _clk_)                                                                     \
    static struct spi_operation read_fast_##_dummy_##_size_##_clk_##_4b_ = { SPI_IF_READ_FAST, SPI_CMD_FAST_READ_4B, \
        _dummy_, _size_, _clk_ }

#define READ_FAST(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_
#define READ_FAST_4B(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_##_4b_
// -----------------------------------------------------------------------------
#define SET_READ_DUAL(_dummy_, _size_, _clk_)                                                                        \
    static struct spi_operation read_dual_##_dummy_##_size_##_clk_ = { SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, \
        _size_, _clk_ }

#define READ_DUAL(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_READ_QUAD(_dummy_, _size_, _clk_)                                                                        \
    static struct spi_operation read_quad_##_dummy_##_size_##_clk_ = { SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, \
        _size_, _clk_ }

#define READ_QUAD(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_READ_DUAL_ADDR(_dummy_, _size_, _clk_)                                                 \
    static struct spi_operation read_dual_addr_##_dummy_##_size_##_clk_ = { SPI_IF_READ_DUAL_ADDR, \
        SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define READ_DUAL_ADDR(_dummy_, _size_, _clk_) read_dual_addr_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_READ_QUAD_ADDR(_dummy_, _size_, _clk_)                                                 \
    static struct spi_operation read_quad_addr_##_dummy_##_size_##_clk_ = { SPI_IF_READ_QUAD_ADDR, \
        SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define READ_QUAD_ADDR(_dummy_, _size_, _clk_) read_quad_addr_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_WRITE_STD(_dummy_, _size_, _clk_)                                                                         \
    static struct spi_operation write_std_##_dummy_##_size_##_clk_ = { SPI_IF_WRITE_STD, SPI_CMD_PP, _dummy_, _size_, \
        _clk_ }

#define SET_WRITE_STD_4B(_dummy_, _size_, _clk_)                                                                       \
    static struct spi_operation write_std_##_dummy_##_size_##_clk_##_4b_ = { SPI_IF_WRITE_STD, SPI_CMD_PP_4B, _dummy_, \
        _size_, _clk_ }


#define WRITE_STD(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_
#define WRITE_STD_4B(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_##_4b_

// -----------------------------------------------------------------------------
#define SET_WRITE_DUAL(_dummy_, _size_, _clk_)                                                                 \
    static struct spi_operation write_dual_##_dummy_##_size_##_clk_ = { SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, \
        _dummy_, _size_, _clk_ }

#define WRITE_DUAL(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_WRITE_QUAD(_dummy_, _size_, _clk_)                                                                 \
    static struct spi_operation write_quad_##_dummy_##_size_##_clk_ = { SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, \
        _dummy_, _size_, _clk_ }

#define WRITE_QUAD(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_WRITE_DUAL_ADDR(_dummy_, _size_, _clk_)                                                  \
    static struct spi_operation write_dual_addr_##_dummy_##_size_##_clk_ = { SPI_IF_WRITE_DUAL_ADDR, \
        SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ }

#define WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) write_dual_addr_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_WRITE_QUAD_ADDR(_dummy_, _size_, _clk_)                                                  \
    static struct spi_operation write_quad_addr_##_dummy_##_size_##_clk_ = { SPI_IF_WRITE_QUAD_ADDR, \
        SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ }

#define WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) write_quad_addr_##_dummy_##_size_##_clk_
// -----------------------------------------------------------------------------
#define SET_ERASE_SECTOR(_dummy_, _size_, _clk_)                                                                    \
    static struct spi_operation erase_sector_##_dummy_##_size_##_clk_ = { SPI_IF_ERASE_SECTOR, SPI_CMD_SE, _dummy_, \
        _size_, _clk_ }

#define SET_ERASE_SECTOR_4B(_dummy_, _size_, _clk_)                                                                 \
    static struct spi_operation erase_sector_##_dummy_##_size_##_clk_##_4b_ = { SPI_IF_ERASE_SECTOR, SPI_CMD_SE_4B, \
        _dummy_, _size_, _clk_ }


#define ERASE_SECTOR(_dummy_, _size_, _clk_) erase_sector_##_dummy_##_size_##_clk_
#define ERASE_SECTOR_4B(_dummy_, _size_, _clk_) erase_sector_##_dummy_##_size_##_clk_##_4b_

// -----------------------------------------------------------------------------
#define SET_ERASE_CHIP(_dummy_, _size_, _clk_)                                                                  \
    static struct spi_operation erase_chip_##_dummy_##_size_##_clk_ = { SPI_IF_ERASE_CHIP, SPI_CMD_BE, _dummy_, \
        _size_, _clk_ }

#define ERASE_CHIP(_dummy_, _size_, _clk_) erase_chip_##_dummy_##_size_##_clk_

/* *************************************************************************** */
SET_READ_STD(0, INFINITE, 40);
SET_READ_STD_4B(0, INFINITE, 40);

SET_READ_FAST(1, INFINITE, 104);

SET_READ_FAST_4B(1, INFINITE, 104);

// -----------------------------------------------------------------------------
SET_WRITE_STD(0, 256, 50);

SET_WRITE_STD_4B(0, 256, 50);

// -----------------------------------------------------------------------------
SET_ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104);

SET_ERASE_SECTOR_4B(0, SPI_IF_ERASE_SECTOR, 104);

/* *************************************************************************** */
static struct spi_info spi_info_table[] = {
    /* boot tag */
    { "null", { 0, 0, 0 }, 3, 0, 0, 0, { 0 }, { 0 }, { 0 }, },

    /* name        id                id_len chipsize(Bytes) erasesize */
    /* Spansion */
    {"S25FL128S or S25FS128S", {0x01, 0x20, 0x18}, 3, (_64K * 256),  _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"S25FL256S", {0x01, 0x02, 0x19}, 3, (_64K * 512),  _64K, 3,
        {&READ_STD_4B(0, INFINITE, 40), &READ_FAST_4B(1, INFINITE, 104), 0},
        {&WRITE_STD_4B(0, 256, 50), 0},
        {&ERASE_SECTOR_4B(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"S25FS512S", {0x01, 0x02, 0x20}, 3, (_256K * 256),  _256K, 3,
        {&READ_STD_4B(0, INFINITE, 40), &READ_FAST_4B(1, INFINITE, 104), 0},
        {&WRITE_STD_4B(0, 256, 50), 0},
        {&ERASE_SECTOR_4B(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    /* Micron */
    {"MT25QU512AB",   {0x20, 0xBB, 0x20}, 3, (_64K * 1024), _64K, 3,
        {&READ_STD_4B(0, INFINITE, 40), &READ_FAST_4B(1, INFINITE, 104), 0},
        {&WRITE_STD_4B(0, 256, 50), 0},
        {&ERASE_SECTOR_4B(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"MT25QU128AB",   {0x20, 0xBB, 0x18}, 3, (_64K * 256), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"N25Q256",   {0x20, 0xBA, 0x19}, 3, (_64K * 512), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    /* Macronix */
    {"MX25U6473F", { 0xC2, 0x25, 0x37 }, 3, (_64K * 128), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"MX25U12835F", { 0xC2, 0x25, 0x38 }, 3, (_64K * 256), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"MX25U51245G or MX66U51235F", { 0xC2, 0x25, 0x3A }, 3, (_64K * 1024), _64K, 3,
        {&READ_STD_4B(0, INFINITE, 40), &READ_FAST_4B(1, INFINITE, 104), 0},
        {&WRITE_STD_4B(0, 256, 50), 0},
        {&ERASE_SECTOR_4B(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    /* Winbond */ // ID
    {"W25Q64FW", { 0xEF, 0x60, 0x17 }, 3, (_64K * 128), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    {"W25Q128FW", { 0xEF, 0x60, 0x18 }, 3, (_64K * 256), _64K, 3,
        {&READ_STD(0, INFINITE, 40), &READ_FAST(1, INFINITE, 104), 0},
        {&WRITE_STD(0, 256, 50), 0},
        {&ERASE_SECTOR(0, SPI_IF_ERASE_SECTOR, 104), 0}},

    { 0, { 0 }, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, },
};
/* *************************************************************************** */
char *int_to_size(unsigned long long size)
{
    int ix;
    static char buffer[20] = {0};
    char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"};
    int offset;

    for (ix = 0; (ix < 5) && !(size & 0x3FF) && size; ix++) {
        size = (size >> 10);
    }
    offset = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, fmt[ix], size);
    ASSERT_RET(!(offset < 0 || offset >= sizeof(buffer)), buffer);
    return buffer;
}
/* *************************************************************************** */
struct spi_info *spi_serach_ids_mxu(unsigned char ids[8], int len)
{
    struct spi_info *info = NULL;
    struct spi_info *fit_info = NULL;

    if (8 < len) {
        DBG_BUG("The len of ID is too long\n");
        return (struct spi_info *)NULL;
    }

    for (info = spi_info_table; info->name != NULL; info++) {
        if (memcmp(info->id, ids, info->id_len))
            continue;

        if (fit_info == NULL || fit_info->id_len < info->id_len) {
            fit_info = info;
        }
    }
    return fit_info;
}
/* *************************************************************************** */
void spi_search_rw_mxu(struct spi_info *spiinfo, struct spi_operation *spiop_rw, unsigned int iftype,
    unsigned int max_dummy, int is_read)
{
    int ix = 0;
    int ret;
    struct spi_operation **spiop = NULL;
    struct spi_operation **fitspiop = NULL;

    ASSERT_NOT_RET(NULL != spiinfo);
    ASSERT_NOT_RET(NULL != spiop_rw);

    for (fitspiop = spiop = (is_read ? spiinfo->read : spiinfo->write); (*spiop) && ix < MAX_SPI_OP; spiop++, ix++) {
        DBG_MSG("dump[%d] %s iftype:0x%02X\n", ix, (is_read ? "read" : "write"), (*spiop)->iftype);

        if (((*spiop)->iftype & iftype) && ((*spiop)->dummy <= max_dummy) && (*fitspiop)->iftype < (*spiop)->iftype) {
            fitspiop = spiop;
        }
    }
    ret = memcpy_s(spiop_rw, sizeof(struct spi_operation), (*fitspiop), sizeof(struct spi_operation));
    ASSERT_NOT_RET((0 == ret));
}
/* *************************************************************************** */
void spi_get_erase_mxu(struct spi_info *spiinfo, struct spi_operation *spiop_erase, unsigned int *erasesize)
{
    int ix;
    int ret = 0;

    ASSERT_NOT_RET(NULL != spiinfo);
    ASSERT_NOT_RET(NULL != spiop_erase);
    ASSERT_NOT_RET(NULL != erasesize);

    (*erasesize) = spiinfo->erasesize;
    for (ix = 0; ix < MAX_SPI_OP; ix++) {
        if (spiinfo->erase[ix] == NULL)
            break;

        ret =
            memcpy_s(&spiop_erase[ix], sizeof(struct spi_operation), spiinfo->erase[ix], sizeof(struct spi_operation));
        if (0 != ret) {
            DBG_BUG("memcpy error\n");
            return;
        }

        switch (spiop_erase[ix].size) {
            case SPI_IF_ERASE_SECTOR:
                spiop_erase[ix].size = spiinfo->erasesize;
                break;
            case SPI_IF_ERASE_CHIP:
                spiop_erase[ix].size = spiinfo->chipsize;
                break;
            default:
                spiop_erase[ix].size = 0;
                break;
        }
        DBG_MSG("erase[%d]->erasesize:%s, cmd:0x%02X\n", ix, int_to_size(spiop_erase[ix].size), spiop_erase[ix].cmd);

        if ((int)(spiop_erase[ix].size) < _2K) {
            DBG_BUG("erase block size mistaken: spi->erase[%d].size:%s\n", ix, int_to_size(spiop_erase[ix].size));
        }

        if (spiop_erase[ix].size < (*erasesize)) {
            (*erasesize) = spiop_erase[ix].size;
        }
    }
}
/* *************************************************************************** */
struct spi_tag {
    char name[16];

    unsigned char id[8];
    unsigned int id_len;

    unsigned long chipsize;
    unsigned int erasesize;
    unsigned int addrcycle;

    struct spi_operation read[MAX_SPI_OP];
    struct spi_operation write[MAX_SPI_OP];
    struct spi_operation erase[MAX_SPI_OP];
};
